﻿
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Polly;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace MicroConfig.NetCore.Client
{
    public class MicroConfigManager
    {
        MicroConfigOptions options;

        public MicroConfigManager()
        {
            items = new ConcurrentDictionary<string, ConfigItem>();
            options = new MicroConfigOptions();
        }

        private ConcurrentDictionary<string, ConfigItem> items;

        public ConfigItem this[string key]
        {
            get
            {
                return GetItem(key);
            }

        }

        public ConfigItem GetItem(string key)
        {
            return items.GetOrAdd(key, new ConfigItem(options.Environment));
        }

        /// <summary>
        /// 更新配置项
        /// </summary>
        /// <param name="params"></param>
        private void UpdateItem(UpdateItemParams @params)
        {
            var item = GetItem(@params.Key);
            item.Value(@params.Env, @params.Value);
        }

        /// <summary>
        /// 同步配置
        /// </summary>
        /// <param name="data"></param>
        private void SyncItems(Dictionary<string, Dictionary<string, string>> data)
        {
            foreach (var kv in data)
            {
                var item = GetItem(kv.Key);
                foreach (var value in kv.Value)
                {
                    item.Value(value.Key, value.Value);
                }
            }
        }

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="action"></param>
        /// <returns></returns>
        public async Task InitAsync(Action<MicroConfigOptions> action)
        {
            action(options);
            if (string.IsNullOrEmpty(options.ProjectName))
            {
                throw new Exception($"{nameof(options.ProjectName)}不能为空");
            }
            if (string.IsNullOrEmpty(options.SignalRUrl))
            {
                throw new Exception($"{nameof(options.SignalRUrl)}不能为空");
            }
            if (string.IsNullOrEmpty(options.Environment))
            {
                throw new Exception($"{nameof(options.Environment)}不能为空");
            }

            HubConnection hubConnection = new HubConnectionBuilder()
                .WithUrl(options.SignalRUrl, connectionOptions =>
                {
                    var domain = new Uri(options.SignalRUrl).Host;
                    connectionOptions.Cookies.Add(new Cookie("x-token", options.AccessToken ?? "", "/", domain));
                })
                .Build();
            hubConnection.Closed += async (error) =>
            {
                await Policy.Handle<Exception>()
                .WaitAndRetryForeverAsync(options.ReconnectionPolicy)
                .ExecuteAsync(() =>
                  {
                      return hubConnection.StartAsync();
                  });
                await hubConnection.InvokeAsync("RegisterProject", options.ProjectName);
            };

            hubConnection.On<UpdateItemParams>("UpdateItem", UpdateItem);
            hubConnection.On<Dictionary<string, Dictionary<string, string>>>("SyncItems", SyncItems);

            try
            {
                await Policy.Handle<Exception>()
                    .RetryAsync(options.ConnectionRetryCount)
                    .ExecuteAsync(() =>
                    {
                        return hubConnection.StartAsync();
                    });
                if (hubConnection.State == HubConnectionState.Connected)
                {
                    await hubConnection.InvokeAsync("RegisterProject", options.ProjectName);
                }
                else
                {
                    throw new Exception("连接配置中心失败,请检查网络和访问token");
                }
            }
            catch (Exception exp)
            {
                throw exp;
            }
        }
    }
}
